package org.springframework.beans.factory.support;

import com.lry.basic.aop.util.ClassUtil;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.anno.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.PropertyValue;
import org.springframework.beans.factory.util.ClassUtils;
import org.springframework.beans.factory.util.StringUtils;
import org.springframework.beans.factory.util.ValueUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/12/13
 */
public class ConstructorResolver {
    private BeanFactory beanFactory;
    public ConstructorResolver(BeanFactory beanFactory){
        this.beanFactory = beanFactory;
    }

    public Object autowireConstructor(String beanName, BeanDefinition bd, Constructor<?>[] ctors) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        //最终使用的构造器
        Constructor<?> constructorToUse = null;
        //最终使用的构造器的参数
        Object[] argsToUse = null;
        Constructor<?>[] candidates = ctors;

        if(null==candidates){
            candidates = bd.getBeanClass().getDeclaredConstructors();
        }

        //只有一个构造器
        if (candidates.length == 1  && !bd.hasConstructorArgumentValues()) {
            Constructor<?> uniqueCandidate = candidates[0];
            //并且是默认构造
            if (uniqueCandidate.getParameterCount() == 0) {
                return bd.getBeanClass().newInstance();
            }
        }

        //是否需要自动装配，如果有构造器或者装配模式是构造器装配都要进行自动装配
        //即springbean 会自动装配给参数

        //如果只有constructorArgumentValues，但是没有ctors，也不是构造器装配模式，autowiring=false
        //他只会找和constructorArgumentValues参数一摸一样的构造器
        boolean autowiring = (ctors != null ||
                bd.getResolvedAutowireMode() == BeanFactory.AUTOWIRE_CONSTRUCTOR);

        //最小参数个数
        int minNrOfArgs = bd.getConstructorArgumentValues()==null?0:bd.getConstructorArgumentValues().size();

        //最小类型差异权重，此值越大差异越大，初始化为最大差异
        int minTypeDiffWeight = Integer.MAX_VALUE;

        //模棱两可的构造器集合，用于存储spring都不确定的构造器集合
        Set<Constructor<?>> ambiguousConstructors = null;

        //遍历构造器
        for (Constructor<?> candidate : candidates) {
            //构造器参数类型数组
            Class<?>[] paramTypes = candidate.getParameterTypes();
            //已经有最终构造器&&已经有最终构造器参数&&最终构造器参数长度>当前构造器参数长度（直接跳出循环，使用constructorToUse）
            if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {
                break;
            }

            //当前构造器参数长度<构造器参数的最小个数，这种情况当前构造器肯定不合格，直接抛弃当前构造器
            if (paramTypes.length < minNrOfArgs) {
                continue;
            }

            Object[] arguments = null;
            try {
                arguments = createArgumentArray(candidate,bd,autowiring);
            } catch (Exception e) {
                //若构造器参数不是@Value的&&不在spring容器就抛异常，并且跳过此构造器
                //转换异常也会抛异常
                e.printStackTrace();
                continue;
            }

            int typeDiffWeight = ClassUtils.getAssignabilityWeight(paramTypes,arguments);

            if (typeDiffWeight < minTypeDiffWeight) {
                //记录当前构造器及参数
                constructorToUse = candidate;
                argsToUse = arguments;
                //更新最小差异权重
                minTypeDiffWeight = typeDiffWeight;
                //重置ambiguousConstructors为null
                ambiguousConstructors = null;
            }
            //如果已经有了最终的构造器可供使用&&当前构造器计算出来的权重==最小差异权重
            else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
                //这下spring就为难了，他把这两个构造器加到模糊集合了
                if (ambiguousConstructors == null) {
                    ambiguousConstructors = new LinkedHashSet<>();
                    ambiguousConstructors.add(constructorToUse);
                }
                ambiguousConstructors.add(candidate);
            }
        }
        if (constructorToUse == null) {
            throw new RuntimeException(beanName+
                    "  Could not resolve matching constructor ");
        }else if (ambiguousConstructors != null && !bd.isLenientConstructorResolution()) {
            throw new RuntimeException("Ambiguous constructor matches found in bean '" + beanName + "' "+ ambiguousConstructors);
        }
        Objects.requireNonNull(argsToUse, beanName+ " Unresolved constructor arguments");

        return constructorToUse.newInstance(argsToUse);
    }
    //argsToUse: 涉及@Value,@Autowired,springBean,constructorArgumentValues，autowiring
//    private Object[] createArgumentArray(Constructor<?> candidate, BeanDefinition bd, boolean autowiring)throws Exception{
//        //autowiring=true 自动装配，会解析@Value,@Autowired,springBean
//        //autowiring=false,手动装配，不会解析@Value,@Autowired,springBean，只要解析constructorArgumentValues
//        if(autowiring){
//            //如果是自动注入并且还有constructorArgumentValues，以constructorArgumentValues优先
//            Parameter[] params = candidate.getParameters();
//            Object[] arguments = new Object[params.length];
//            List<?> constructorArgumentValues = bd.getConstructorArgumentValues();
//
//            //引入spring-core的asm技术，因为params[i].getName() 获得的参数名字是arg0 arg1
//            LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
//            String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(candidate);
//
//            for (int i=0;i<params.length;i++){
//                //基本类型转包装类型
//                Class<?>paramType = params[i].getType();
//                if(ClassUtils.primitiveTypeToWrapperMap.containsKey(paramType)){
//                    paramType = ClassUtils.primitiveTypeToWrapperMap.get(paramType);
//                }
//                if(params[i].isAnnotationPresent(Value.class)){
//                    //拿到@Value的值
//                    String value = params[i].getAnnotation(Value.class).value();
//                    //如果constructorArgumentValues有此参数&&并且类型一致，优先使用constructorArgumentValues
//                    if(null!=constructorArgumentValues&&
//                       constructorArgumentValues.size()>i&&
//                       null!=constructorArgumentValues.get(i)&&
//                       paramType.isAssignableFrom(constructorArgumentValues.get(i).getClass())){
//                        arguments[i] = constructorArgumentValues.get(i);
//                    }else{
//                        //使用@Value的值（getBaseValue防止 object to int/float/... error）
//                        arguments[i] = ValueUtils.getBaseValue(params[i].getType(),value);
//                    }
//                }else{
//                    if(null!=constructorArgumentValues&&
//                            constructorArgumentValues.size()>i&&
//                            null!=constructorArgumentValues.get(i)&&
//                            paramType.isAssignableFrom(constructorArgumentValues.get(i).getClass())){
//                        arguments[i] = constructorArgumentValues.get(i);
//                    }else{
//                        arguments[i] = beanFactory.getBean(parameterNames[i]);
//                    }
//                }
//            }
//            return arguments;
//        }
//        //autowiring为false的话constructorArgumentValues必定不为null
//        else{
//            return bd.getConstructorArgumentValues().toArray(new Object[0]);
//        }
//    }





    private Object[] createArgumentArray(Constructor<?> candidate, BeanDefinition bd, boolean autowiring)throws Exception{
        //autowiring=true 自动装配，会解析@Value,@Autowired,springBean
        //autowiring=false,手动装配，不会解析@Value,@Autowired,springBean，只要解析constructorArgumentValues
        if(autowiring){
            //如果是自动注入并且还有constructorArgumentValues，以constructorArgumentValues优先
            Parameter[] params = candidate.getParameters();
            Object[] arguments = new Object[params.length];

            List<PropertyValue> constructorArgumentValues = bd.getConstructorArgumentPropertyValues();

            //引入spring-core的asm技术，因为params[i].getName() 获得的参数名字是arg0 arg1
            LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(candidate);

            for (int i=0;i<params.length;i++){
                //基本类型转包装类型
                Class<?>paramType = params[i].getType();
                if(ClassUtils.primitiveTypeToWrapperMap.containsKey(paramType)){
                    paramType = ClassUtils.primitiveTypeToWrapperMap.get(paramType);
                }

                if(params[i].isAnnotationPresent(Value.class)){
                    //拿到@Value的值
                    String value = params[i].getAnnotation(Value.class).value();
                    Object realValue = null;
                    if(null!=constructorArgumentValues&&constructorArgumentValues.size()>0){
                        realValue = findConstructorArgumentValue(parameterNames[i],constructorArgumentValues);
                    }
                    if(null==realValue){
                        //使用@Value的值（getBaseValue防止 object to int/float/... error）
                        realValue = ValueUtils.getBaseValue(params[i].getType(),value);
                    }
                    arguments[i] = realValue;
                }else{
                    Object realValue = null;
                    if(null!=constructorArgumentValues&&constructorArgumentValues.size()>0){
                        realValue = findConstructorArgumentValue(parameterNames[i],constructorArgumentValues);
                    }
                    if(null==realValue){
                        realValue = beanFactory.getBean(parameterNames[i]);
                    }
                    arguments[i] = realValue;
                }
            }
            return arguments;
        }
        //autowiring为false的话constructorArgumentValues必定不为null
        else{
            return bd.getConstructorArgumentValues().toArray(new Object[0]);
        }
    }

    private Object findConstructorArgumentValue(String parameterName, List<PropertyValue> constructorArgumentValues) {
        for (PropertyValue pv:constructorArgumentValues) {
             if(parameterName.equals(pv.getName())){
                 return pv.getValue();
             }
        }
        return null;
    }


}
